<?php

namespace app\utils\library;

class Mask
{
    const CARD = 1;     //身份证
    const PHONE = 2;    //手机号
    const NAME = 3;     //姓名
    
    public static function createInstance(array $args = [])
    {
        // 实例的类名
        $class = static::class;
        // 实例化类
        $instance = new $class(...$args);
        // 实例初始化操作
        $instance->initialization();
        // 返回类实例的对象
        return $instance;
    }
    
    
    private function initialization()
    {
    }
    
    /**
     * 姓名脱敏
     * @param $name
     * @return string
     */
    public function name($name)
    {
        return $this->formatName($name);
    }
    
    /**
     * 身份证号码脱敏
     * @param string $idCard
     * @return string|string[]
     */
    public function idCard(string $idCard)
    {
        if (empty($idCard)) {
            return '';
        }
        $len = strlen($idCard);
        return substr_replace($idCard, str_repeat('*', ($len - 8)), 6, $len - 8);
    }
    
    /**
     * 手机号码脱敏
     * @param $phone
     * @return string|string[]
     */
    public function phone($phone)
    {
        if (empty($phone)) {
            return '';
        }
        $len = strlen($phone);
        return substr_replace($phone, str_repeat('*', ($len - 7)), 7, $len - 7);
    }
    
    /**
     * Notes: 脱敏格式化字符串
     * @param string $str 需要脱敏字符
     * @param int $type 内容类型
     * @return mixed|string
     */
    private function formatData(string $str, int $type)
    {
        $res = '';
        if (empty($str) || empty($type)) {
            return $res;
        }
        
        $len = strlen($str);
        switch ($type) {
            case self::CARD:
                $res = substr_replace($str, str_repeat('*', ($len - 8)), 6, $len - 8);
                break;
            case self::PHONE:
                $res = substr_replace($str, str_repeat('*', ($len - 7)), 7, $len - 7);
                break;
            case self::NAME:
                $res = $this->formatName($str);
                break;
            default :
                $res = $str;
                break;
        }
        return $res;
    }
    
    /**
     * Notes: 批量脱敏格式化数据
     * @param array $data 格式化字符数组
     * @param int $type 内容类型
     * @return array|mixed
     */
    private function bacthFormatData(array $data, int $type)
    {
        $ret = [];
        if (empty($data) || !is_array($data) || empty($type)) {
            return $ret;
        }
        
        switch ($type) {
            case self::CARD :
                foreach ($data as $item){
                    $len = strlen($item);
                    $ret[$item] = substr_replace($item, str_repeat('*', ($len - 8)), 6, $len - 8);
                }
                break;
            case self::PHONE :
                foreach ($data as $item) {
                    $len = strlen($item);
                    $ret[$item] = substr_replace($item, str_repeat('*', ($len - 7)), 3, $len - 7);
                }
                break;
            case self::NAME :
                foreach ($data as $item) {
                    $ret[$item] = $this->formatName($item);
                }
                break;
            default :
                $ret = $data;
                break;
        }
        return $ret;
    }
    
    
    /**
     * Notes:脱敏格式化名称
     * @param string $str 需要脱敏字符
     * @return string
     */
    private function formatName(string $str): string
    {
        $res = '';
        if (empty($str)) {
            return $res;
        }
        
        $len = mb_strlen($str);
        $res = $str;
        if ($len == 2){
            $res = $this->stringTrim($res, '*', 0, 1);
        } elseif ($len == 3) {
            $res = $this->stringTrim($res, '*', 0, 1, 1, 1);
        } elseif ($len > 3 && $len <= 6) {
            $res = $this->stringTrim($res, '*', 0, 1, 2, 2);
        } elseif ($len > 6) {
            $res = $this->stringTrim($res, '*', 0, 2, 3, 3);
        }
        return $res;
    }
    
    
    /**
     * Notes:中文脱敏替换
     * @param string $str 脱敏字符
     * @param string $replace 替换字符
     * @param int $start 头部位置
     * @param int $startLen 头部替换长度
     * @param int $end 尾部位置
     * @param int $endLen 尾部替换长度
     * @return string
     */
    private function stringTrim(string $str, string $replace = '*', int $start = 0, int $startLen = 0, int $end = 0, int $endLen = 0): string
    {
        $str = mb_substr($str, $start, $startLen) . str_repeat($replace, (mb_strlen($str) - ($startLen + $endLen))) .mb_substr($str, -$end, $endLen);
        return $str;
    }
    
    
    /**
     * Notes:数据脱敏格式化
     * @param array|string $datas 需要格式化的数据内容
     * @param int $type 内容类型 1：身份证，2：手机号，3：姓名
     * @return array|mixed|string|string[]
     */
    public function dataMark($datas, int $type = self::NAME)
    {
        if (!is_array($datas)) {
            return $this->formatData($datas, $type);
        }
        return $this->bacthFormatData($datas, $type);
    }
    
    /**
     * 对银行卡号进行掩码处理
     * 掩码规则头4位,末尾余数位不变，中间4的整数倍字符用星号替换，并且用每隔4位空格隔开
     * @author 晓风<215628355@qq.com>
     * @param  string $bankCardNo 银行卡号
     * @return string             掩码后的银行卡号
     */
    function formatBankCardNo($bankCardNo){
        //每隔4位分割为数组
        $split = str_split($bankCardNo,4);
        //头和尾保留，其他部分替换为星号
        $split = array_fill(1,count($split) - 2,"****") + $split;
        ksort($split);
        //合并
        return implode(" ",$split);
    }
    
    /**
     * 对手机号进行掩码处理
     * 手机号掩码的规则是末尾4位，开头余数位不变，中间4的整数倍字符用星号替换
     * @author 晓风<215628355@qq.com>
     * @param string $mobile 手机号
     * @return string
     */
    function formatMobile($mobile){
        //颠倒顺序每隔4位分割为数组
        $split = str_split(strrev($mobile),4);
        //头和尾保留，其他部分替换为星号
        $split = array_fill(1,count($split) - 2,"****") + $split;
        ksort($split);
        //合并颠倒顺序
        return strrev( implode("",$split));
    }
    
    /**
     * 姓名第一个字掩码
     * @author 晓风<215628355@qq.com>
     * @param string $true_name 真实姓名
     * @return string
     */
    function formatTrueName($true_name){
        return "*" . mb_substr($true_name,1);
    }
    
    /**
     * 对身份证进行掩码
     * 掩码规则是显示头两位和末尾1位，中间星号
     * @author 晓风<215628355@qq.com>
     * @param string $id_card 身份证号
     * @return string
     */
    function formatIdCard($id_card){
        //每隔1位分割为数组
        $split = str_split($id_card,1);
        //头2位和尾保留，其他部分替换为星号
        $split = array_fill(2,count($split) - 3,"*") + $split;
        ksort($split);
        //合并
        return implode("",$split);
    }
    
    /**
     * 数据脱敏
     * @param $string 需要脱敏值
     * @param int $start 开始
     * @param int $length 结束
     * @param string $re 脱敏替代符号
     * @return bool|string
     * 例子:
     * dataDesensitization('18811113683', 3, 4); //188****3683
     * dataDesensitization('乐杨俊', 0, -1); //**俊
     */
    function dataDesensitization($string, $start = 0, $length = 0, $re = '*')
    {
        if (empty($string)){
            return false;
        }
        $strarr = array();
        $mb_strlen = mb_strlen($string);
        while ($mb_strlen) {//循环把字符串变为数组
            $strarr[] = mb_substr($string, 0, 1, 'utf8');
            $string = mb_substr($string, 1, $mb_strlen, 'utf8');
            $mb_strlen = mb_strlen($string);
        }
        $strlen = count($strarr);
        $begin = $start >= 0 ? $start : ($strlen - abs($start));
        $end = $last = $strlen - 1;
        if ($length > 0) {
            $end = $begin + $length - 1;
        } elseif ($length < 0) {
            $end -= abs($length);
        }
        for ($i = $begin; $i <= $end; $i++) {
            $strarr[$i] = $re;
        }
        if ($begin >= $end || $begin >= $last || $end > $last) return false;
        return implode('', $strarr);
    }
}


//调用测试demo
// $mask = new Mask();
// $name = '我是中文名称';
// $card = '333333333333333333';
// $phone = '15012345678';
//
// $names = ['我是中文名称1', '我是中文名称2'];
// $cards = ['333333333333333333', '222222222222222222'];
// $phones = ['15012345678', '13012345678'];
//
// echo "<pre>";
//
// echo $mask->dataMark($card, 1)."</br>";
// echo $mask->dataMark($phone, 2)."</br>";
// echo $mask->dataMark($name, 3)."</br>";
//
// print_r($mask->dataMark($cards, 1));
// print_r($mask->dataMark($phones, 2));
// print_r($mask->dataMark($names, 3));
//
// echo "</pre>";





